/** Copyright 2014, Ross Mahony. Some rights reserved. See COPYING. */
package controllers

import java.util.Calendar 

import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.format.Formats._ 
import play.api.data.Forms._
import play.api.db.slick._
import play.api.db.slick.{ Session => SlickSession }

import views._
import models._

case class RoadmapPlan(
  customerId: Long, measures: Seq[RoadmapMeasure], discounts: Seq[RoadmapDiscount]
)

/** [[play.api.mvc.Controller]] for the roadmaps detail view. */
object Roadmaps extends Controller {

  val genForm = Form(
    single(
      "budget" -> of[Double]
    )
  )

 /** RoadmapPlan Form definition. */
  val editPlanForm: Form[RoadmapPlan] = Form(
    mapping(
      "customerId" -> longNumber,
      "measures" -> seq(
        mapping(
          "roadmapId" -> optional(longNumber),
          "roadmapMeasureId" -> longNumber,
          "status" -> nonEmptyText,
          "savingCost" -> of[Double], 
          "installCost" -> of[Double],
          "savingCO2" -> of[Double]
        )(RoadmapMeasure.apply)(RoadmapMeasure.unapply)
      ),
      "discounts" -> seq(
        mapping(
          "roadmapId" -> optional(longNumber),
          "name" -> nonEmptyText,
          "discount" -> of[Double],
          "notes" -> text
        )(RoadmapDiscount.apply)(RoadmapDiscount.unapply)
      )
    )(RoadmapPlan.apply)(RoadmapPlan.unapply)
  )

  /** Retrieve all measures. */
  def getAllMeasures()(implicit s: SlickSession) = { 
    val measures = MeasureDAO.list
    Logger.debug(f"Retrieved all measures, size:[${measures.size}%d]")
    measures
  }

  /** Retrieve roadmap measures. */
  def getRoadmapMeasures(customerId: Long)(implicit s: SlickSession) = {
    val measures = RoadmapDAO.listMeasures(customerId)
    Logger.debug(f"Retrieved measures, size:[${measures.size}%d]")
    measures
  }

  /** Retrieve roadmap discounts. */
  def getDiscounts(customerId: Long)(implicit s: SlickSession) = {
    val discounts = RoadmapDAO.listDiscounts(customerId)
    Logger.debug(f"Retrieved discounts, size:[${discounts.size}%d]")
    discounts
  }

 /**
  * Action to display the roadmaps detail page.
  *
  * @param id Customer ID
  */
  def index(id: Option[Long]) = DBAction { implicit rs =>
    CustomerDAO.loadCustomer(id).map { customer =>
      Logger.debug(f"Found customer id:[${customer.id.get}%d]")

      RoadmapDAO.findByCustomerId(customer.id.get).map { roadmap =>
        Logger.debug(f"Found roadmap id:[${roadmap.customerId}%d]")
        
        val roadmapMeasures = getRoadmapMeasures(roadmap.customerId)
        val discounts = getDiscounts(roadmap.customerId)

        val existingPlan = RoadmapPlan(
          customer.id.get,
          measures = for (rm <- roadmapMeasures) yield rm._2,
          discounts = discounts
        )
        
        Ok(html.roadmap.index(customer, roadmap, roadmapMeasures,
          discounts, editPlanForm.fill(existingPlan), 
          getAllMeasures(), Tips.getTips(), Tips.getTipComments(), Tips.getTipLikes()))
      }.getOrElse {
        // if roadmap does not exist redirect to generate page
        Ok(html.roadmap.generate(customer, genForm, budgetOptions))
      }   
    }.getOrElse(NotFound)
  }

 /**
  * Action to display the generate roadmap page.
  *
  * @param id Customer ID
  */
  def generate(id: Long) = DBAction { implicit rs =>
    genForm.bindFromRequest.fold(
      formWithErrors => {
        CustomerDAO.loadCustomer(Some(id)).map { customer =>
          BadRequest(html.roadmap.generate(customer, formWithErrors, budgetOptions))
        }.getOrElse(NotFound)
      },
      budget => {
        Logger.debug(f"budget:[$budget%2.2f]")
        // create roadmap plan
        RoadmapDAO.insert(Roadmap(id, "Bronze", budget, Calendar.getInstance().getTime(), None))

        // list all measures
        val measures = MeasureDAO.list
        Logger.debug(f"total measure count in db:[${measures.size}%d]")
        var totalCost = 0d;
        val newMeasures = measures takeWhile { measure => 
            totalCost = totalCost + measure.installCost          
            totalCost < budget
        }
        Logger.debug(f"measure count to be saved:[${newMeasures.size}%d] totalCost:[$totalCost%2.2f]")
        
        // create roadmap messures
        newMeasures.foreach { measure =>
          RoadmapDAO.insertMeasure(RoadmapMeasure(Some(id), 
            measure.id.get, "In-progress", measure.savingCost, measure.installCost, measure.savingCO2))
        }

        Redirect(routes.Roadmaps.index(Some(id)))
      }
    )
  }

 /**
  * Selection of valid budgets
  */
  def budgetOptions: Seq[(String, String)] = {
    List(("999999999", "no budget"), ("500", "< 500"),("1000", "< 1000"),
      ("2000", "< 2000"),("5000", "< 5000"),("10000", "< 10000"))
  }

 /**
  * Handle form submission.
  */
  def editPlan = DBAction { implicit rs =>
    Logger.debug("in edit plan")

    editPlanForm.bindFromRequest.fold(
      formWithErrors => {
        Logger.error("error submitting form:" + formWithErrors("customerId").value.get)
        val m = formWithErrors("measures").value
        Logger.error("size:" + m.size)

        CustomerDAO.loadCustomer(Some(formWithErrors("customerId").value.get.toLong)).map { customer =>
          Logger.debug(f"Found customer id:[${customer.id.get}%d]")

          RoadmapDAO.findByCustomerId(customer.id.get).map { roadmap =>
            Logger.debug(f"Found roadmap id:[${roadmap.customerId}%d]")

            BadRequest(html.roadmap.index(customer, roadmap, 
              getRoadmapMeasures(roadmap.customerId), getDiscounts(roadmap.customerId), formWithErrors, 
              getAllMeasures(), Tips.getTips(), Tips.getTipComments(), Tips.getTipLikes()))

          }.getOrElse(NotFound)
        }.getOrElse(NotFound)
      },
      editPlan => {
        //Ok(html.contact.summary(editPlan))
        Logger.debug("editPlan:" + editPlan.customerId + " measure size:" + editPlan.measures.size)

        val measuresDelCount = RoadmapDAO.deleteMeasures(editPlan.customerId) 
        Logger.debug("number roadmap measure rows deleted:" + measuresDelCount)
        
        val discountsDelCount = RoadmapDAO.deleteDiscounts(editPlan.customerId) 
        Logger.debug("number discount rows deleted:" + discountsDelCount)
        
        editPlan.measures.foreach( m => {
          Logger.debug("inserting measure:" + m)
          RoadmapDAO.insertMeasure(m)
        })
        
        editPlan.discounts.foreach( d => {
          Logger.debug("inserting discount:" + d)
          RoadmapDAO.insertDiscount(d)
        })

        Redirect(routes.Roadmaps.index(Some(editPlan.customerId)))
      }  
    )
  }
        
}